#include "highgui.h"
#include "cv.h"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <FL/Fl_File_Chooser.H>
#include "Utils.hpp"
#include "Shape.hpp"

#define RECTANGLE 1
#define TRIANGLE 2
#define CIRCLE 3

using namespace std;

void mouseCallback(int event, int x, int y, int flags, void* param);
bool drawShape = true;
bool deformingPic = false;
bool deformPicture = false;
int chosenShapeNum;
vector<int> xs,ys;
Shape* actualChosenShape;


int main( int argc, char** argv ) {

	//opens a file chooser windows
	Fl_File_Chooser fileChooser(".","*.jpg",0,"File Chooser");
	fileChooser.show();
	while( fileChooser.shown() ) { Fl::wait(); }
	IplImage* img = cvLoadImage(fileChooser.value(),CV_LOAD_IMAGE_GRAYSCALE);
	fileChooser.rescan();

	cvNamedWindow("Original Picture", CV_WINDOW_AUTOSIZE);
	cvShowImage( "Original Picture", img );

	chosenShapeNum = 0;
	//keeps asking the user for a desired shape until legal (1-3)
	while (chosenShapeNum < 1 || chosenShapeNum > 3) {
		cout << "What shape would you like to use for the filter ? \n"
				"1 - Rectangle\n"
				"2 - Triangle\n"
				"3 - Circle" << endl;
		cin >> chosenShapeNum;
	}

	switch (chosenShapeNum) {
		case RECTANGLE: {
			cout << "Click 2 times on the picture: first on the left highest point\n"
					"and second on the right lowest point" << endl;
		}
		break;
		case TRIANGLE: {
			cout << "Click 3 times on the picture: first select the center of the triangle, second and third clicks\n"
					"will determine the size of each side in the Equilateral Triangle" << endl;
		}
		break;
		case CIRCLE: {
			cout << "Click 2 times on the picture: first select the center of the circle and second click\n"
					"on the perimeter of the circle" << endl;
		}
		break;
	}

    IplImage* temp = cvCloneImage( img );
    cvNamedWindow( "Original Picture" );
    cvSetMouseCallback("Original Picture",mouseCallback,(void*) temp);

    while( 1 ) {
      cvShowImage("Original Picture" , temp );
      if (cvWaitKey( 15 )==27) {
    	  break;
      }
    }

    //show the original image again
    cvShowImage( "Original Picture", img );

	int chosenFilter = 0;
	//keeps asking the user for a desired filter until legal (1-2)
	while (chosenFilter != 1 && chosenFilter !=2 ) {
		cout << "What filter would you like to use ? \n"
				"1 - Low Pass Filter\n"
				"2 - High Pass filter" << endl;
		cin >> chosenFilter;
	}

    int chosenFilterType = 0;
    //keeps asking the user for a desired filter type until legal (1-2)
	while (chosenFilterType != 1 && chosenFilterType !=2 ) {
		cout << "Would you like to use a sharp filter boundary or a smooth filter boundary? \n"
    			"1 - Sharp Filter\n"
    			"2 - Smooth Filter" << endl;
		cin >> chosenFilterType;
	}

	//performs the fft, applies the filter and performs the inverse fft
	IplImage* imgAfterFilter=Utils::fft(img, chosenFilterType, chosenFilter, actualChosenShape);
    cvNamedWindow("Image After Filter",CV_WINDOW_AUTOSIZE);
    cvShowImage("Image After Filter",imgAfterFilter);
    cvWaitKey(0);

    //saves the image into a file
    string saveImage;
    cout << "Do you want to save the image ? (y\\n)" << endl;
    cin >> saveImage;
    if (saveImage.compare("y")==0) {
    	string imageName;
    	cout << "What do you want the name of the image to be ?" << endl;
    	cin >> imageName;
    	cvSaveImage(imageName.c_str(),imgAfterFilter);

    }

    //compares the original image with the filtered image - "Compared Image"
    string compareImages;
    cout << "Do you want to compare the images ? (y\\n)" << endl;
    cin >> compareImages;
    if (compareImages.compare("y")==0) {
    	IplImage* comparedResult = cvCloneImage(img);
    	int height    = img->height;
		int width     = img->width;
		int step      = img->widthStep;
		int channels  = img->nChannels;
		uchar* origData            = (uchar *)img->imageData;
		uchar* afterFilterData     = (uchar *)imgAfterFilter->imageData;
		uchar* comparedResultData = (uchar *)comparedResult->imageData;

		for(int i=0;i<height;i++) {
			for(int j=0;j<width;j++) {
				for(int k=0;k<channels;k++) {
					comparedResultData[i*step+j*channels+k] = abs(
							origData[i*step+j*channels+k] -
							afterFilterData[i*step+j*channels+k]);
				}
			}
		}
		cvNamedWindow("Compared Image",CV_WINDOW_AUTOSIZE);
		cvShowImage("Compared Image",comparedResult);
		cvWaitKey(0);
		cvReleaseImage( &comparedResult);
		cvDestroyWindow( "Compared Image" );
    }

    cvReleaseImage( &img );
    cvReleaseImage( &temp );
    cvReleaseImage( &imgAfterFilter);
    cvDestroyWindow( "Image After Filter");
    cvDestroyWindow( "Original Picture" );
}


void mouseCallback (int event, int x, int y, int flags, void* param) {
	IplImage* image = (IplImage*) param;

	//draws the chosen shape over the original image just for illustration
	if (drawShape) {

		switch( event ) {
		  case CV_EVENT_LBUTTONDOWN: {
			  switch (chosenShapeNum) {
				case RECTANGLE: {
					if (xs.size() != 2 && ys.size() != 2) {
						xs.push_back(x);
						ys.push_back(y);
					} else {
						drawShape = false;
						Utils::drawRect(image, xs.at(0), ys.at(0), xs.at(1), ys.at(1));
						actualChosenShape = new Rectangle(xs.at(0), ys.at(0), xs.at(1), ys.at(1));
					}
				}
				break;
				case TRIANGLE: {
					if (xs.size() != 3 && ys.size() != 3) {
						xs.push_back(x);
						ys.push_back(y);
					} else {
						drawShape = false;
						actualChosenShape = Utils::drawAndCreateTriangle(image,xs.at(0), ys.at(0),xs.at(1), ys.at(1), xs.at(2), ys.at(2));
					}
				}
				break;
				case CIRCLE: {
					if (xs.size() != 2 && ys.size() != 2) {
						xs.push_back(x);
						ys.push_back(y);
					} else {
						drawShape = false;
						actualChosenShape = Utils::drawAndCreateCircle(image, xs.at(0), ys.at(0),xs.at(1), ys.at(1));
					}
				}
				break;
			}
		  }
		  break;
	   }
	}
}
